﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using facebook.Components;
using facebook;
using Veracruz.Facebook;
using Veracruz.Facebook.Data;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
using Veracruz.IdentityModel;
using System.Globalization;
using System.Security.Cryptography;
using System.Web;

namespace Veracruz.Facebook
{
    public static class FacebookHelper // TODO : Change to internal.
    {
        #region methods

        internal static List<string> ParameterDictionaryToList(IEnumerable<KeyValuePair<string, string>> parameterDictionary)
        {
            List<string> list = new List<string>();
            foreach (KeyValuePair<string, string> pair in parameterDictionary)
            {
                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}", new object[] { pair.Key }));
            }
            return list;
        }
        private static string GenerateSignature(IDictionary<string, string> parameters)
        {
            StringBuilder builder = new StringBuilder();
            List<string> list = ParameterDictionaryToList(parameters);
            list.Sort();
            foreach (string str in list)
            {
                builder.Append(string.Format(CultureInfo.InvariantCulture, "{0}={1}", new object[] { str, parameters[str] }));
            }
            builder.Append("2a83b6c2ce9fa638808985e0ae49048b");
            byte[] buffer = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(builder.ToString().Trim()));
            builder = new StringBuilder();
            foreach (byte num in buffer)
            {
                builder.Append(num.ToString("x2", CultureInfo.InvariantCulture));
            }
            return builder.ToString();
        }
        private static string CreateHTTPParameterList(string applicationKey, IDictionary<string, string> parameterList)
        {
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> pair in parameterList)
            {
                builder.Append(pair.Key);
                builder.Append("=");
                builder.Append(HttpUtility.UrlEncode(pair.Value));
                builder.Append("&");
            }
            builder.Remove(builder.Length - 1, 1);
            return builder.ToString();
        }
        public static string GetFqlQueryUrl(Session session)
        {
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            //dictionary.Add("method", "facebook.fql.query");
            dictionary.Add("query", string.Format("SELECT uid, pic_small, pic FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = {0})", session.UserId));
            dictionary.Add("api_key", "446a7f1afab93a763e0c3016e03a72a2");
            dictionary.Add("call_id", DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture));
            dictionary.Add("sig", GenerateSignature(dictionary));
            dictionary.Add("v", "1.0");
            dictionary.Add("session_key", session.SessionKey);
            //dictionary.Add("format", "JSON");

            return string.Concat("http://api.facebook.com/restserver.php?method=facebook.fql.query&", CreateHTTPParameterList("20890198761", dictionary));
        }

        private static UserInfo CreateUserInfo(facebook.Schema.user user, string email)
        {
            return new UserInfo
            {
                AuthenticationSchemeClaim = AuthenticationSchemeClaimValue.Facebook,
                FirstName = user.first_name,
                LastName = user.last_name,
                Name = user.name,
                Sex = user.sex,
                UserId = user.uid.Value.ToString(),
                Email = email,
                HomeTown = (user.hometown_location.city == null ? string.Empty : user.hometown_location.city),
            };
        }
        private static API CreateAPI()
        {
            facebook.API l_api = new facebook.API();
            l_api.ApplicationKey = Constants.ApplicationKey;
            l_api.Secret = Constants.Secret;
            l_api.IsDesktopApplication = false;
            return l_api;
        }
        private static API CreateAPI(string authentificationToken)
        {
            API l_api = CreateAPI();
            l_api.CreateSession(authentificationToken);
            return l_api;
        }
        internal static API CreateAPI(Session session)
        {
            API l_api = CreateAPI();
            l_api.SessionKey = session.SessionKey;
            l_api.UserId = Convert.ToInt32(session.UserId);
            return l_api;
        }

        public static void Register(Session session, string email)
        {
            API l_api = CreateAPI(session);
            Dictionary<string, string> l_properties = new Dictionary<string, string>();
            l_properties.Add(UserObjectType.Email, email);
            long l_userKey = l_api.data.createObject(UserObjectType.Name, l_properties);

            l_api.data.setAssociation(EmailAssociation.Name, l_api.UserId, l_userKey, email, DateTime.Now);
            l_api.data.setAssociation(UsersAssociation.Name, Constants.ApplicationId, l_api.UserId, email, DateTime.Now);
        }
        public static string GetEmail(Session session, long userId)
        {
            API l_api = CreateAPI(session);
            string l_query = EmailAssociation.GetEmailQuery(userId);
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            return l_xdoc.Root.Value;
        }
        public static UserInfo GetUserInfo(Session session, long userId)
        {
            API l_api = CreateAPI(session);
            facebook.Schema.user l_user = l_api.users.getInfo((int)userId);
            return CreateUserInfo(l_user, GetEmail(session, userId));
        }
        public static UserInfo GetUserInfo(string authentificationToken, out Session session)
        {
            API l_api = CreateAPI();
            l_api.CreateSession(authentificationToken);

            session = new Session(l_api.UserId, l_api.SessionKey);

            facebook.Schema.user l_user = l_api.users.getInfo(); // TODO : fql query to get with only one request the user email.
            return CreateUserInfo(l_user, GetEmail(session));
        }
        public static string GetEmail(Session session)
        {
            API l_api = CreateAPI(session);
            string l_query = EmailAssociation.GetEmailQuery(session.UserId);
            string l_response = l_api.fql.query(l_query);

            XDocument l_xdoc = XDocument.Parse(l_response);
            //XmlNamespaceManager l_namespaceManager = new XmlNamespaceManager(new NameTable());
            //l_namespaceManager.AddNamespace("fb", "http://api.facebook.com/1.0/");
            //XElement l_xElement = l_xdoc.Root.XPathSelectElement("//fb:_data", l_namespaceManager);
            return l_xdoc.Root.Value;
        }
        public static Dictionary<string, string> GetFriendsEmails(Session session, int uid)
        {
            API l_api = CreateAPI(session);
            string l_query = EmailAssociation.GetEmailsQuery(uid);
            string l_response = l_api.fql.query(l_query);

            Dictionary<string, string> __friends = new Dictionary<string, string>();

            XDocument l_xdoc = XDocument.Parse(l_response);
            XmlNamespaceManager l_namespaceManager = new XmlNamespaceManager(new NameTable());
            l_namespaceManager.AddNamespace("fb", "http://api.facebook.com/1.0/");

            foreach (XElement item in l_xdoc.Root.XPathSelectElements("//fb:app.email", l_namespaceManager))
            {
                //XName __uid = XName.Get("uid", "fb");
                string __uid = item.Elements(XName.Get("uid", "http://api.facebook.com/1.0/")).First().Value;
                //string __uid2 = item.Elements("uid").First().Value;
                string __email = item.Elements(XName.Get("_data", "http://api.facebook.com/1.0/")).First().Value;

                __friends.Add(__uid, __email);
            }

            //return l_xdoc.Root.Value;
            return __friends;

        }
        public static Dictionary<string, string> GetFriendsEmails(Session session)
        {
            return GetFriendsEmails(session, session.UserId);
        }
        public static UserInfo GetUserInfo(Session session, string userName)
        {
            //API l_api = CreateAPI(session);
            //string l_query = UsersAssociation.Queries.GetUserIdByEmail(email);
            //string l_response = l_api.fql.query(l_query);
            //XDocument l_xdoc = XDocument.Parse(l_response);
            //if (String.IsNullOrEmpty(l_xdoc.Root.Value))
            //{
            //    return null;
            //}
            //else
            //{
            //    int l_uid = Convert.ToInt32(l_xdoc.Root.Value);
            //    facebook.Schema.user l_user = l_api.users.getInfo(l_uid);
            //    return CreateUserInfo(l_user, email);            
            //}

            API l_api = CreateAPI(session);
            string l_query = UsersAssociation.Queries.GetUserIdByFullName(userName);
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            if (String.IsNullOrEmpty(l_xdoc.Root.Value))
            {
                return null;
            }
            else
            {
                int l_uid = Convert.ToInt32(l_xdoc.Root.Value);
                facebook.Schema.user l_user = l_api.users.getInfo(l_uid);
                return CreateUserInfo(l_user, string.Empty);
            }


        }
        public static void Reset(Session session)
        {
            UserObjectType.Reset(session);
            EmailAssociation.Reset(session);
            UsersAssociation.Reset(session);
        }
        public static UserInfo[] GetAllUserInfos(Session session, int pageIndex, int pageSize)
        {
            API l_api = CreateAPI(session);
            string l_query = UsersAssociation.Queries.GetAllUserIdentities(pageIndex, pageSize);
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            List<UserInfo> l_userInfoList = new List<UserInfo>();
            //XmlNamespaceManager l_namespaceManager = new XmlNamespaceManager(new NameTable());
            //l_namespaceManager.AddNamespace("fb", "http://api.facebook.com/1.0/");
            //IEnumerable<XElement> l_xElements = l_xdoc.Root.XPathSelectElements("fb:fql_query_response/fb:app.users", l_namespaceManager);
            //List<UserIdentity> l_userIdentityList = new List<UserIdentity>();
            //foreach (XElement xElement in l_xElements)
            //{
            //    UserIdentity l_userIdentity = new UserIdentity();
            //    l_userIdentity.UserId = xElement.Descendants(new XName()).;
            //    l_userIdentity.Email = "";

            //    l_userIdentityList.Add(l_userIdentity);
            //}
            var l_result = from user in l_xdoc.Root.Elements(XName.Get("app.users", "http://api.facebook.com/1.0/"))
                           select new UserIdentity
                           {
                               UserId = Convert.ToInt32(user.Element(XName.Get("uid", "http://api.facebook.com/1.0/")).Value),
                               Email = user.Element(XName.Get("_data", "http://api.facebook.com/1.0/")).Value
                           };
            string l_uids = string.Empty;
            foreach (UserIdentity item in l_result)
            {
                l_uids += string.Concat(item.UserId, ",");
            }
            IList<facebook.Schema.user> l_userList = l_api.users.getInfo(l_uids.Substring(0, l_uids.Length - 1));
            for (int i = 0; i < l_userList.Count; i++)
            {
                facebook.Schema.user l_user = l_userList[i];
                UserIdentity l_userIdentity = l_result.Single<UserIdentity>(userIdentity => userIdentity.UserId == l_user.uid);
                l_userInfoList.Add(CreateUserInfo(l_user, l_userIdentity.Email));
            }
            return l_userInfoList.ToArray();
        }
        public static UserInfo[] FindUserInfosByEmail(Session session, string emailToMatch, int pageIndex, int pageSize)
        {
            API l_api = CreateAPI(session);
            string l_query = UsersAssociation.Queries.GetAllUserIdentitiesByEmail(emailToMatch, pageIndex, pageSize);
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            List<UserInfo> l_userInfoList = new List<UserInfo>();
            var l_result = from user in l_xdoc.Root.Elements(XName.Get("app.users", "http://api.facebook.com/1.0/"))
                           select new UserIdentity
                           {
                               UserId = Convert.ToInt32(user.Element(XName.Get("uid", "http://api.facebook.com/1.0/")).Value),
                               Email = user.Element(XName.Get("_data", "http://api.facebook.com/1.0/")).Value
                           };
            string l_uids = string.Empty;
            foreach (UserIdentity item in l_result)
            {
                l_uids += string.Concat(item.UserId, ",");
            }

            if (!String.IsNullOrEmpty(l_uids))
            {
                IList<facebook.Schema.user> l_userList = l_api.users.getInfo(l_uids.Substring(0, l_uids.Length - 1));
                for (int i = 0; i < l_userList.Count; i++)
                {
                    facebook.Schema.user l_user = l_userList[i];
                    UserIdentity l_userIdentity = l_result.Single<UserIdentity>(userIdentity => userIdentity.UserId == l_user.uid);
                    l_userInfoList.Add(CreateUserInfo(l_user, l_userIdentity.Email));
                }
            }
            return l_userInfoList.ToArray();
        }
        public static UserInfo[] FindUserInfosByFullName(Session session, string fullNameTomatch, int pageIndex, int pageSize)
        {
            API l_api = CreateAPI(session);
            string l_query = UsersAssociation.Queries.GetAllUserIdentitiesByFullName(fullNameTomatch, pageIndex, pageSize);
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            List<UserInfo> l_userInfoList = new List<UserInfo>();
            var l_result = from user in l_xdoc.Root.Elements(XName.Get("app.users", "http://api.facebook.com/1.0/"))
                           select new UserIdentity
                           {
                               UserId = Convert.ToInt32(user.Element(XName.Get("uid", "http://api.facebook.com/1.0/")).Value),
                               Email = string.Empty//= user.Element(XName.Get("_data", "http://api.facebook.com/1.0/")).Value
                           };
            string l_uids = string.Empty;
            foreach (UserIdentity item in l_result)
            {
                l_uids += string.Concat(item.UserId, ",");
            }

            if (!String.IsNullOrEmpty(l_uids))
            {
                IList<facebook.Schema.user> l_userList = l_api.users.getInfo(l_uids.Substring(0, l_uids.Length - 1));
                for (int i = 0; i < l_userList.Count; i++)
                {
                    facebook.Schema.user l_user = l_userList[i];
                    UserIdentity l_userIdentity = l_result.Single<UserIdentity>(userIdentity => userIdentity.UserId == l_user.uid);
                    l_userInfoList.Add(CreateUserInfo(l_user, l_userIdentity.Email));
                }
            }
            return l_userInfoList.ToArray();
        }
        public static int GetNumberOfUsersOnline(Session session)
        {
            API l_api = CreateAPI(session);
            string l_query = UserTable.Queries.GetNumberOfUsersOnline();
            string l_response = l_api.fql.query(l_query);
            XDocument l_xdoc = XDocument.Parse(l_response);
            return l_xdoc.Root.Elements(XName.Get("user", "http://api.facebook.com/1.0/")).Count<XElement>();
        }

        public static void GetAlbums(Session session)
        {
            API l_api = CreateAPI(session);
            l_api.photos.getAlbums();
        }

        #endregion
        #region archive

        public static UserInfo[] GetFriends(Session session, string userId)
        {
           API l_api = CreateAPI(session);

            List<UserInfo> l_userInfoList = new List<UserInfo>();
            IList<facebook.Schema.user> l_userList = l_api.friends.getUserObjects();

            Dictionary<string, string> __pairs = FacebookHelper.GetFriendsEmails(session);

            foreach (facebook.Schema.user user in l_userList)
            {
                UserInfo l_userInfo = new UserInfo();
                l_userInfo.AuthenticationSchemeClaim = AuthenticationSchemeClaimValue.Facebook;
                //l_userInfo.Email = l_user.
                if(__pairs.ContainsKey(user.uid.ToString()))
                {
                    l_userInfo.Email = __pairs[user.uid.ToString()];
                }
                l_userInfo.FirstName = user.first_name;
                l_userInfo.HomeTown = user.hometown_location.city + ", " + user.hometown_location.country;
                l_userInfo.LastName = user.last_name;
                l_userInfo.Name = user.name;
                l_userInfo.Sex = user.sex;
                l_userInfo.UserId = user.uid.Value.ToString();
                l_userInfo.PictureUrl = user.pic;
                l_userInfo.SmallPictureUrl = user.pic_small;
                l_userInfoList.Add(l_userInfo);
            }
            return l_userInfoList.ToArray();
        }
        public static void GetMembers(string sessionKey, string userId, string groupId)
        {
            API l_api = CreateAPI();
            l_api.SessionKey = sessionKey;
            l_api.UserId = Convert.ToInt32(userId);

            facebook.Schema.group_members l = l_api.groups.getMembers(Convert.ToInt64(groupId));

            return;
        }

        #endregion
    }
}
